/**
	*******************************************************************************************************************
	*******************************************************************************************************************
	*	作者： 		曾彬芮
	* 
	*	日期： 		2021-10-22
	*
	*	版本： 		V3.0（根据实验要求更改了代码，变为了类似有限自动机的方式）
	*
	*	说明： 		词法分析器（将标识符和关键字糅合，更改一种输出方式，对应Token_T中的值）
	*******************************************************************************************************************
	*******************************************************************************************************************
**/

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// 能识别的最长标识符/数字
#define MAX_LEN_STRING  50

typedef enum {
	tok_ID=256,		 // 标识符
	tok_INTEGER,	 // 整数
	tok_INT,		 // int
	tok_VOID,		 // void
	tok_CONST,		 // const
	tok_IF,			 // if
	tok_ELSE,		 // else
	tok_WHILE,		 // while
	tok_BREAK,		 // break
	tok_CONTINUE,	 // continue
	tok_RETURN,		 // return
	tok_ADD,		 // +
	tok_SUB,		 // -
	tok_MUL,		 // *
	tok_DIV,		 // /
	tok_MODULO,		 // %
	tok_LESS,		 // <
	tok_LESSEQ,		 // <=
	tok_GREAT,		 // >
	tok_GREATEQ,	 // >=
	tok_NOTEQ,		 // !=
	tok_EQ,		 	 // ==
	tok_NOT,		 // !
	tok_AND, 		 // &&
	tok_OR,			 // ||
	tok_ASSIGN,		 // =
	tok_LPAR,		 // (
	tok_RPAR,		 // )
	tok_LBRACKET,	 // {
	tok_RBRACKET,	 // }
	tok_LSQUARE,	 // [
	tok_RSQUARE,	 // ]
	tok_COMMA,		 // ,
	tok_SEMICOLON	 // ;
}Token_T;
typedef enum {identifier=0, constant=1, border=2, operator=3, done=4} Case_Type;

int typeJudge(char str);
void LexicalAnalyze(char file[], int size);

int typeJudge(char str){
    if((str>='a' && str<='z') || (str>='A' && str<='Z') || str=='_')
    {
        return identifier;
    }
    else if(str>='0' && str<='9')
    {
        return constant;
    }
    else if(str=='('||str==')'||str=='{'||str=='}'||str=='['||str==']'||str==','||str==';')
    {
        return border;
    }
    else if(str=='+'||str=='-'||str=='*'||str=='/'||str=='%'||str=='<'||str=='>'|| \
            str=='!'||str=='='||str=='&'||str=='|')
    {
        return operator;
    }else{
        return done;
    }
}

void LexicalAnalyze(char file[], int size){
    int count = 0, store = 0;
    // flags
    int ifJudge = 0, hasNum, allowOther = 1, c_now=0, c_past=0; 
    char number[MAX_LEN_STRING];
    char string[MAX_LEN_STRING];

    while(count < size){
        // {string=0, constant=1, border=2, operator=3}
        switch(typeJudge(file[count]))
        {
            // 标识符识别
            case identifier:
            while(((file[count]>='a'&& file[count]<='z') || (file[count]>='A'&& file[count]<='Z') || file[count]=='_'))
            {
                string[store] = file[count];
                store++; count++;
                // 标识符除第一位以外，其他位可以为数字
                while(file[count]>='0' && file[count]<='9'){
                    string[store] = file[count];
                    store++; count++;
                }
                ifJudge = 1;
            }
            hasNum = 0;
            if(ifJudge==1)
            {
                c_past = c_now;
                if(strcmp(string, "int")==0){
                    printf("%d\n",tok_INT); c_now++;
                }
                if(strcmp(string, "void")==0){
                    printf("%d\n",tok_VOID); c_now++;
                }
                if(strcmp(string, "const")==0){
                    printf("%d\n",tok_CONST); c_now++;
                }
                if(strcmp(string, "if")==0){
                    printf("%d\n",tok_IF); c_now++;
                }
                if(strcmp(string, "else")==0){
                    printf("%d\n",tok_ELSE); c_now++;
                }
                if(strcmp(string, "while")==0){
                    printf("%d\n",tok_WHILE); c_now++;
                }
                if(strcmp(string, "break")==0){
                    printf("%d\n",tok_BREAK); c_now++;
                }
                if(strcmp(string, "continue")==0){
                    printf("%d\n",tok_CONTINUE); c_now++;
                }
                if(strcmp(string, "return")==0){
                    printf("%d\n",tok_RETURN); c_now++;
                }
                if(c_now==c_past){
                    if((string[0]!='x'||string[0]!='X') && file[count-store-1]!='0'){
                        printf("%d %s\n", tok_ID, string);
                    }
                }
                memset(string, 0, sizeof(string));
                store = 0;
            }
            ifJudge = 0; c_now = c_past = 0;
            break;
            
            // 常量识别
            case constant:
            if(file[count] == '0' && (file[count+1] == 'x' || file[count+1] == 'X')){
                while((file[count+2] >= '0' && file[count+2] <= '9' ) || \
                (file[count+2] >= 'A' && file[count+2] <= 'F' ) || \
                (file[count+2] >= 'a' && file[count+2] <= 'f' ))
                {  
                    number[store] = file[count];
                    count++; store++;
                }
                number[store] = file[count];
                number[++store] = file[++count];
            }else{
            // 判断8进制与10进制
                while(file[count] >= '0' && file[count] <= '9'){
                    number[store] = file[count];
                    store++ ; count++;
                    if(file[count] == '.' && allowOther == 1){
                        number[store] = file [count];
                        store++; count++; allowOther = 0;
                    }
                }
            }
            // 如果常量是标识符的一部分，则省略
            if(store!=0){
                if((file[count-store-1]>='a'&&file[count-store-1]<='z')||\
                (file[count-store-1]>='A'&&file[count-store-1]<='Z')||\
                file[count-store-1]=='_')
                {
                    memset(number, 0, sizeof(number));
                    store = 0;  
                }else{
                    printf("%d %s\n", tok_INTEGER, number);
                    memset(number, 0, sizeof(number));
                    store = 0;  
                }
            }
            allowOther = 1;break;

            // 界符识别
            case border:
            if(file[count]=='(') printf("%d\n",tok_LPAR);
            if(file[count]==')') printf("%d\n",tok_RPAR);
            if(file[count]=='{') printf("%d\n",tok_LBRACKET);
            if(file[count]=='}') printf("%d\n",tok_RBRACKET);
            if(file[count]=='[') printf("%d\n",tok_LSQUARE);
            if(file[count]==']') printf("%d\n",tok_RSQUARE);
            if(file[count]==',') printf("%d\n",tok_COMMA);
            if(file[count]==';') printf("%d\n",tok_SEMICOLON);
            count ++;break;

            // 运算符识别
            case operator:
            if(file[count]=='+') printf("%d\n",tok_ADD);
            if(file[count]=='-') printf("%d\n",tok_SUB);
            if(file[count]=='*') printf("%d\n",tok_MUL);
            if(file[count]=='/') printf("%d\n",tok_DIV);
            if(file[count]=='%') printf("%d\n",tok_MODULO);
            if(file[count]=='<'){
                if(file[count+1]=='='){
                    printf("%d\n",tok_LESSEQ); count++;
                }else{
                    printf("%d\n",tok_LESS);
                }
            }
            if(file[count]=='>'){
                if(file[count+1]=='='){
                    printf("%d\n",tok_GREATEQ); count++;
                }else{
                    printf("%d\n",tok_GREAT);
                }
            }
            if(file[count]=='!'){
                if(file[count+1]=='='){
                    printf("%d\n",tok_NOTEQ); count++;
                }else{
                    printf("%d\n",tok_NOT);
                }
            }
            if(file[count]=='='){
                if(file[count+1]=='='){
                    printf("%d\n",tok_EQ); count++;
                }else{
                    printf("%d\n",tok_ASSIGN);
                }
            }
            if(file[count]==file[count+1]=='&'){
                printf("%d\n",tok_AND); count++;
            }
            if(file[count]==file[count+1]=='|'){
                printf("%d\n",tok_OR); count++;
            }
            count++;break;
            default:count++;break;
        }
    }
}

int main(int argc, char ** argv)
{
    int cur;
    FILE *fp = fopen(argv[1],"r");

    // 获得文件的大小
    fseek(fp,0L,SEEK_END);
    int filesize = ftell(fp);
    char fileString[filesize];

    // 重定向，使指针重新指向文件头
    rewind(fp);
    printf("File size is: %d\n",filesize);
    putchar('\n');
    // 将文件中的字符串复制给fileString
    for(cur=0; cur < filesize; cur++){
        fscanf(fp,"%c",&fileString[cur]);
    }
    fclose(fp);

    LexicalAnalyze(fileString, filesize); 
    
    return 0;
}
